package com.hejie.userservice.service.impl;

import com.hejie.common.exception.BusinessException;
import com.hejie.common.exception.ErrorCode;
import com.hejie.common.utils.JwtTokenProvider;
import com.hejie.userservice.dto.UserDTO;
import com.hejie.userservice.dto.UserLoginRequest;
import com.hejie.userservice.dto.UserRegisterRequest;
import com.hejie.userservice.dto.UserUpdateRequest;
import com.hejie.userservice.entity.User;
import com.hejie.userservice.repository.UserRepository;
import com.hejie.userservice.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Resource
    private UserRepository userRepository;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private JwtTokenProvider jwtTokenProvider;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private KafkaTemplate<String, Object> kafkaTemplate;

    private static final String USER_CACHE_KEY = "user:info:";
    private static final String TOKEN_BLACKLIST_KEY = "token:blacklist:";
    private static final long CACHE_EXPIRE_DAYS = 7;
    private static final long TOKEN_EXPIRE_HOURS = 24;
    private static final long REFRESH_TOKEN_EXPIRE_DAYS = 30;

    @Override
    @Transactional
    public UserDTO register(UserRegisterRequest registerRequest) {
        // 检查用户名是否已存在
        if (userRepository.existsByUsernameAndDeletedFalse(registerRequest.getUsername())) {
            throw new BusinessException(ErrorCode.USERNAME_ALREADY_EXISTS);
        }

        // 检查邮箱是否已存在
        if (userRepository.existsByEmailAndDeletedFalse(registerRequest.getEmail())) {
            throw new BusinessException(ErrorCode.EMAIL_ALREADY_EXISTS);
        }

        // 检查手机号是否已存在
        if (registerRequest.getPhone() != null && userRepository.existsByPhoneAndDeletedFalse(registerRequest.getPhone())) {
            throw new BusinessException(ErrorCode.PHONE_ALREADY_EXISTS);
        }

        // 创建用户实体
        User user = new User();
        BeanUtils.copyProperties(registerRequest, user);
        user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
        user.setRole(User.UserRole.USER);
        user.setStatus(User.UserStatus.ACTIVE);

        // 保存用户
        User savedUser = userRepository.save(user);
        log.info("用户注册成功: username={}, id={}", savedUser.getUsername(), savedUser.getId());

        // 发送用户注册成功事件到Kafka
        kafkaTemplate.send("user-register-topic", savedUser.getId());

        // 转换为DTO返回
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(savedUser, userDTO);
        return userDTO;
    }

    @Override
    public Map<String, String> login(UserLoginRequest loginRequest) {
        try {
            // 认证用户
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            loginRequest.getUsername(),
                            loginRequest.getPassword()
                    )
            );

            SecurityContextHolder.getContext().setAuthentication(authentication);
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            User user = userRepository.findByUsernameAndDeletedFalse(userDetails.getUsername())
                    .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));

            // 检查用户状态
            if (user.getStatus() != User.UserStatus.ACTIVE) {
                throw new BusinessException(ErrorCode.USER_ACCOUNT_NOT_ACTIVE);
            }

            // 生成JWT令牌
            String accessToken = jwtTokenProvider.generateToken(user.getUsername(), user.getRole().name());
            String refreshToken = jwtTokenProvider.generateRefreshToken(user.getUsername());

            // 缓存用户信息
            redisTemplate.opsForValue().set(
                    USER_CACHE_KEY + user.getId(),
                    user,
                    CACHE_EXPIRE_DAYS,
                    TimeUnit.DAYS
            );

            // 返回令牌
            Map<String, String> tokens = new HashMap<>();
            tokens.put("accessToken", accessToken);
            tokens.put("refreshToken", refreshToken);
            tokens.put("userId", user.getId().toString());
            tokens.put("username", user.getUsername());
            tokens.put("role", user.getRole().name());

            return tokens;
        } catch (Exception e) {
            log.error("用户登录失败: username={}, error={}", loginRequest.getUsername(), e.getMessage());
            throw new BusinessException(ErrorCode.INVALID_USERNAME_PASSWORD);
        }
    }

    @Override
    public UserDTO getUserById(Long userId) {
        // 先从缓存获取
        User user = (User) redisTemplate.opsForValue().get(USER_CACHE_KEY + userId);
        if (user == null) {
            // 缓存未命中，从数据库获取
            user = userRepository.findById(userId)
                    .filter(u -> !u.isDeleted())
                    .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

            // 更新缓存
            redisTemplate.opsForValue().set(
                    USER_CACHE_KEY + userId,
                    user,
                    CACHE_EXPIRE_DAYS,
                    TimeUnit.DAYS
            );
        }

        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(user, userDTO);
        return userDTO;
    }

    @Override
    public UserDTO getUserByUsername(String username) {
        User user = userRepository.findByUsernameAndDeletedFalse(username)
                .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(user, userDTO);
        return userDTO;
    }

    @Override
    @Transactional
    public UserDTO updateUser(Long userId, UserUpdateRequest updateRequest) {
        User user = userRepository.findById(userId)
                .filter(u -> !u.isDeleted())
                .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

        // 更新用户信息
        if (updateRequest.getNickname() != null) {
            user.setNickname(updateRequest.getNickname());
        }
        if (updateRequest.getAvatar() != null) {
            user.setAvatar(updateRequest.getAvatar());
        }
        if (updateRequest.getBio() != null) {
            user.setBio(updateRequest.getBio());
        }
        if (updateRequest.getPhone() != null && !updateRequest.getPhone().equals(user.getPhone())) {
            // 检查新手机号是否已被使用
            if (userRepository.existsByPhoneAndDeletedFalse(updateRequest.getPhone())) {
                throw new BusinessException(ErrorCode.PHONE_ALREADY_EXISTS);
            }
            user.setPhone(updateRequest.getPhone());
        }

        User updatedUser = userRepository.save(user);
        log.info("用户信息更新成功: userId={}", userId);

        // 更新缓存
        redisTemplate.opsForValue().set(
                USER_CACHE_KEY + userId,
                updatedUser,
                CACHE_EXPIRE_DAYS,
                TimeUnit.DAYS
        );

        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(updatedUser, userDTO);
        return userDTO;
    }

    @Override
    @Transactional
    public boolean changePassword(Long userId, String oldPassword, String newPassword) {
        User user = userRepository.findById(userId)
                .filter(u -> !u.isDeleted())
                .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

        // 验证旧密码
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new BusinessException(ErrorCode.OLD_PASSWORD_INCORRECT);
        }

        // 更新密码
        int rows = userRepository.updateUserPassword(userId, passwordEncoder.encode(newPassword));
        if (rows > 0) {
            log.info("用户密码更新成功: userId={}", userId);
            // 清除缓存
            redisTemplate.delete(USER_CACHE_KEY + userId);
            // 发送密码更新事件
            kafkaTemplate.send("user-password-update-topic", userId);
            return true;
        }
        return false;
    }

    @Override
    @Transactional
    public UserDTO updateUserStatus(Long userId, User.UserStatus status) {
        User user = userRepository.findById(userId)
                .filter(u -> !u.isDeleted())
                .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

        int rows = userRepository.updateUserStatus(userId, status);
        if (rows > 0) {
            log.info("用户状态更新成功: userId={}, status={}", userId, status);
            user.setStatus(status);
            // 更新缓存
            redisTemplate.opsForValue().set(
                    USER_CACHE_KEY + userId,
                    user,
                    CACHE_EXPIRE_DAYS,
                    TimeUnit.DAYS
            );
            // 发送用户状态变更事件
            kafkaTemplate.send("user-status-update-topic", userId + "," + status);
        }

        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(user, userDTO);
        return userDTO;
    }

    @Override
    public Page<UserDTO> queryUsers(String keyword, Pageable pageable) {
        Page<User> userPage;
        if (keyword == null || keyword.isEmpty()) {
            userPage = userRepository.findByDeletedFalse(pageable);
        } else {
            // 实际项目中应该使用更复杂的查询条件，这里简化处理
            userPage = userRepository.findByDeletedFalse(pageable);
        }

        return userPage.map(user -> {
            UserDTO dto = new UserDTO();
            BeanUtils.copyProperties(user, dto);
            return dto;
        });
    }

    @Override
    public Map<String, String> refreshToken(String refreshToken) {
        if (!jwtTokenProvider.validateRefreshToken(refreshToken)) {
            throw new BusinessException(ErrorCode.INVALID_REFRESH_TOKEN);
        }

        String username = jwtTokenProvider.getUsernameFromRefreshToken(refreshToken);
        User user = userRepository.findByUsernameAndDeletedFalse(username)
                .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

        // 生成新的访问令牌
        String newAccessToken = jwtTokenProvider.generateToken(username, user.getRole().name());

        Map<String, String> tokens = new HashMap<>();
        tokens.put("accessToken", newAccessToken);
        return tokens;
    }

    @Override
    public void logout(String username) {
        User user = userRepository.findByUsernameAndDeletedFalse(username)
                .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

        // 将当前令牌加入黑名单
        String token = SecurityContextHolder.getContext().getAuthentication().getCredentials().toString();
        redisTemplate.opsForValue().set(
                TOKEN_BLACKLIST_KEY + token,
                "blacklisted",
                TOKEN_EXPIRE_HOURS,
                TimeUnit.HOURS
        );

        // 清除用户缓存
        redisTemplate.delete(USER_CACHE_KEY + user.getId());
        log.info("用户登出成功: username={}", username);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsernameAndDeletedFalse(username)
                .orElseThrow(() -> new UsernameNotFoundException("用户名或密码不正确"));

        return new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                user.getStatus() == User.UserStatus.ACTIVE,
                true,
                true,
                user.getStatus() == User.UserStatus.ACTIVE,
                user.getAuthorities()
        );
    }
}